home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Resources / Developers / XAMPP 1.5.4 / Windows installer / xampp-win32-1.5.4-installer.exe / xampp / php / pear / XML / Unserializer.php < prev    next >
Encoding:
PHP Script  |  2006-04-07  |  27.8 KB  |  856 lines

  1. <?PHP
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3.  
  4. /**
  5.  * XML_Unserializer
  6.  *
  7.  * Parses any XML document into PHP data structures.
  8.  *
  9.  * PHP versions 4 and 5
  10.  *
  11.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  12.  * that is available through the world-wide-web at the following URI:
  13.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  14.  * the PHP License and are unable to obtain it through the web, please
  15.  * send a note to license@php.net so we can mail you a copy immediately.
  16.  *
  17.  * @category   XML
  18.  * @package    XML_Serializer
  19.  * @author     Stephan Schmidt <schst@php.net>
  20.  * @copyright  1997-2005 The PHP Group
  21.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  22.  * @version    CVS: $Id: Unserializer.php,v 1.39 2005/09/28 11:19:56 schst Exp $
  23.  * @link       http://pear.php.net/package/XML_Serializer
  24.  * @see        XML_Unserializer
  25.  */
  26.  
  27. /**
  28.  * uses PEAR error managemt
  29.  */
  30. require_once 'PEAR.php';
  31.  
  32. /**
  33.  * uses XML_Parser to unserialize document
  34.  */
  35. require_once 'XML/Parser.php';
  36.  
  37. /**
  38.  * option: Convert nested tags to array or object
  39.  *
  40.  * Possible values:
  41.  * - array
  42.  * - object
  43.  * - associative array to define this option per tag name
  44.  */
  45. define('XML_UNSERIALIZER_OPTION_COMPLEXTYPE', 'complexType');
  46.  
  47. /**
  48.  * option: Name of the attribute that stores the original key
  49.  *
  50.  * Possible values:
  51.  * - any string
  52.  */
  53. define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY', 'keyAttribute');
  54.  
  55. /**
  56.  * option: Name of the attribute that stores the type
  57.  *
  58.  * Possible values:
  59.  * - any string
  60.  */
  61. define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE', 'typeAttribute');
  62.  
  63. /**
  64.  * option: Name of the attribute that stores the class name
  65.  *
  66.  * Possible values:
  67.  * - any string
  68.  */
  69. define('XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS', 'classAttribute');
  70.  
  71. /**
  72.  * option: Whether to use the tag name as a class name
  73.  *
  74.  * Possible values:
  75.  * - true or false
  76.  */
  77. define('XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME', 'tagAsClass');
  78.  
  79. /**
  80.  * option: Name of the default class
  81.  *
  82.  * Possible values:
  83.  * - any string
  84.  */
  85. define('XML_UNSERIALIZER_OPTION_DEFAULT_CLASS', 'defaultClass');
  86.  
  87. /**
  88.  * option: Whether to parse attributes
  89.  *
  90.  * Possible values:
  91.  * - true or false
  92.  */
  93. define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE', 'parseAttributes');
  94.  
  95. /**
  96.  * option: Key of the array to store attributes (if any)
  97.  *
  98.  * Possible values:
  99.  * - any string
  100.  * - false (disabled)
  101.  */
  102. define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY', 'attributesArray');
  103.  
  104. /**
  105.  * option: string to prepend attribute name (if any)
  106.  *
  107.  * Possible values:
  108.  * - any string
  109.  * - false (disabled)
  110.  */
  111. define('XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND', 'prependAttributes');
  112.  
  113. /**
  114.  * option: key to store the content, if XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE is used
  115.  *
  116.  * Possible values:
  117.  * - any string
  118.  */
  119. define('XML_UNSERIALIZER_OPTION_CONTENT_KEY', 'contentName');
  120.  
  121. /**
  122.  * option: map tag names
  123.  *
  124.  * Possible values:
  125.  * - associative array
  126.  */
  127. define('XML_UNSERIALIZER_OPTION_TAG_MAP', 'tagMap');
  128.  
  129. /**
  130.  * option: list of tags that will always be enumerated
  131.  *
  132.  * Possible values:
  133.  * - indexed array
  134.  */
  135. define('XML_UNSERIALIZER_OPTION_FORCE_ENUM', 'forceEnum');
  136.  
  137. /**
  138.  * option: Encoding of the XML document
  139.  *
  140.  * Possible values:
  141.  * - UTF-8
  142.  * - ISO-8859-1
  143.  */
  144. define('XML_UNSERIALIZER_OPTION_ENCODING_SOURCE', 'encoding');
  145.  
  146. /**
  147.  * option: Desired target encoding of the data
  148.  *
  149.  * Possible values:
  150.  * - UTF-8
  151.  * - ISO-8859-1
  152.  */
  153. define('XML_UNSERIALIZER_OPTION_ENCODING_TARGET', 'targetEncoding');
  154.  
  155. /**
  156.  * option: Callback that will be applied to textual data
  157.  *
  158.  * Possible values:
  159.  * - any valid PHP callback
  160.  */
  161. define('XML_UNSERIALIZER_OPTION_DECODE_FUNC', 'decodeFunction');
  162.  
  163. /**
  164.  * option: whether to return the result of the unserialization from unserialize()
  165.  *
  166.  * Possible values:
  167.  * - true
  168.  * - false (default)
  169.  */
  170. define('XML_UNSERIALIZER_OPTION_RETURN_RESULT', 'returnResult');
  171.  
  172. /**
  173.  * option: set the whitespace behaviour
  174.  *
  175.  * Possible values:
  176.  * - XML_UNSERIALIZER_WHITESPACE_KEEP
  177.  * - XML_UNSERIALIZER_WHITESPACE_TRIM
  178.  * - XML_UNSERIALIZER_WHITESPACE_NORMALIZE
  179.  */
  180. define('XML_UNSERIALIZER_OPTION_WHITESPACE', 'whitespace');
  181.  
  182. /**
  183.  * Keep all whitespace
  184.  */
  185. define('XML_UNSERIALIZER_WHITESPACE_KEEP', 'keep');
  186.  
  187. /**
  188.  * remove whitespace from start and end of the data
  189.  */
  190. define('XML_UNSERIALIZER_WHITESPACE_TRIM', 'trim');
  191.  
  192. /**
  193.  * normalize whitespace
  194.  */
  195. define('XML_UNSERIALIZER_WHITESPACE_NORMALIZE', 'normalize');
  196.  
  197. /**
  198.  * option: whether to ovverride all options that have been set before
  199.  *
  200.  * Possible values:
  201.  * - true
  202.  * - false (default)
  203.  */
  204. define('XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS', 'overrideOptions');
  205.  
  206. /**
  207.  * option: list of tags, that will not be used as keys
  208.  */
  209. define('XML_UNSERIALIZER_OPTION_IGNORE_KEYS', 'ignoreKeys');
  210.  
  211. /**
  212.  * option: whether to use type guessing for scalar values
  213.  */
  214. define('XML_UNSERIALIZER_OPTION_GUESS_TYPES', 'guessTypes');
  215.  
  216. /**
  217.  * error code for no serialization done
  218.  */
  219. define('XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION', 151);
  220.  
  221. /**
  222.  * XML_Unserializer
  223.  *
  224.  * class to unserialize XML documents that have been created with
  225.  * XML_Serializer. To unserialize an XML document you have to add
  226.  * type hints to the XML_Serializer options.
  227.  *
  228.  * If no type hints are available, XML_Unserializer will guess how
  229.  * the tags should be treated, that means complex structures will be
  230.  * arrays and tags with only CData in them will be strings.
  231.  *
  232.  * <code>
  233.  * require_once 'XML/Unserializer.php';
  234.  *
  235.  * //  be careful to always use the ampersand in front of the new operator
  236.  * $unserializer = &new XML_Unserializer();
  237.  *
  238.  * $unserializer->unserialize($xml);
  239.  *
  240.  * $data = $unserializer->getUnserializedData();
  241.  * <code>
  242.  *
  243.  *
  244.  * @category   XML
  245.  * @package    XML_Serializer
  246.  * @author     Stephan Schmidt <schst@php.net>
  247.  * @copyright  1997-2005 The PHP Group
  248.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  249.  * @version    Release: 0.18.0
  250.  * @link       http://pear.php.net/package/XML_Serializer
  251.  * @see        XML_Serializer
  252.  */
  253. class XML_Unserializer extends PEAR
  254. {
  255.    /**
  256.     * list of all available options
  257.     *
  258.     * @access private
  259.     * @var    array
  260.     */
  261.     var $_knownOptions = array(
  262.                                 XML_UNSERIALIZER_OPTION_COMPLEXTYPE,
  263.                                 XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY,
  264.                                 XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE,
  265.                                 XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS,
  266.                                 XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME,
  267.                                 XML_UNSERIALIZER_OPTION_DEFAULT_CLASS,
  268.                                 XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE,
  269.                                 XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY,
  270.                                 XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND,
  271.                                 XML_UNSERIALIZER_OPTION_CONTENT_KEY,
  272.                                 XML_UNSERIALIZER_OPTION_TAG_MAP,
  273.                                 XML_UNSERIALIZER_OPTION_FORCE_ENUM,
  274.                                 XML_UNSERIALIZER_OPTION_ENCODING_SOURCE,
  275.                                 XML_UNSERIALIZER_OPTION_ENCODING_TARGET,
  276.                                 XML_UNSERIALIZER_OPTION_DECODE_FUNC,
  277.                                 XML_UNSERIALIZER_OPTION_RETURN_RESULT,
  278.                                 XML_UNSERIALIZER_OPTION_WHITESPACE,
  279.                                 XML_UNSERIALIZER_OPTION_IGNORE_KEYS,
  280.                                 XML_UNSERIALIZER_OPTION_GUESS_TYPES
  281.                               );
  282.    /**
  283.     * default options for the serialization
  284.     *
  285.     * @access private
  286.     * @var    array
  287.     */
  288.     var $_defaultOptions = array(
  289.                          XML_UNSERIALIZER_OPTION_COMPLEXTYPE         => 'array',                // complex types will be converted to arrays, if no type hint is given
  290.                          XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY       => '_originalKey',         // get array key/property name from this attribute
  291.                          XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE      => '_type',                // get type from this attribute
  292.                          XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS     => '_class',               // get class from this attribute (if not given, use tag name)
  293.                          XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME    => true,                   // use the tagname as the classname
  294.                          XML_UNSERIALIZER_OPTION_DEFAULT_CLASS       => 'stdClass',             // name of the class that is used to create objects
  295.                          XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE    => false,                  // parse the attributes of the tag into an array
  296.                          XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY => false,                  // parse them into sperate array (specify name of array here)
  297.                          XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND  => '',                     // prepend attribute names with this string
  298.                          XML_UNSERIALIZER_OPTION_CONTENT_KEY         => '_content',             // put cdata found in a tag that has been converted to a complex type in this key
  299.                          XML_UNSERIALIZER_OPTION_TAG_MAP             => array(),                // use this to map tagnames
  300.                          XML_UNSERIALIZER_OPTION_FORCE_ENUM          => array(),                // these tags will always be an indexed array
  301.                          XML_UNSERIALIZER_OPTION_ENCODING_SOURCE     => null,                   // specify the encoding character of the document to parse
  302.                          XML_UNSERIALIZER_OPTION_ENCODING_TARGET     => null,                   // specify the target encoding
  303.                          XML_UNSERIALIZER_OPTION_DECODE_FUNC         => null,                   // function used to decode data
  304.                          XML_UNSERIALIZER_OPTION_RETURN_RESULT       => false,                  // unserialize() returns the result of the unserialization instead of true
  305.                          XML_UNSERIALIZER_OPTION_WHITESPACE          => XML_UNSERIALIZER_WHITESPACE_TRIM, // remove whitespace around data
  306.                          XML_UNSERIALIZER_OPTION_IGNORE_KEYS         => array(),                // List of tags that will automatically be added to the parent, instead of adding a new key
  307.                          XML_UNSERIALIZER_OPTION_GUESS_TYPES         => false                   // Whether to use type guessing
  308.                         );
  309.  
  310.    /**
  311.     * current options for the serialization
  312.     *
  313.     * @access public
  314.     * @var    array
  315.     */
  316.     var $options = array();
  317.  
  318.    /**
  319.     * unserialized data
  320.     *
  321.     * @access private
  322.     * @var    string
  323.     */
  324.     var $_unserializedData = null;
  325.  
  326.    /**
  327.     * name of the root tag
  328.     *
  329.     * @access private
  330.     * @var    string
  331.     */
  332.     var $_root = null;
  333.  
  334.    /**
  335.     * stack for all data that is found
  336.     *
  337.     * @access private
  338.     * @var    array
  339.     */
  340.     var $_dataStack  =   array();
  341.  
  342.    /**
  343.     * stack for all values that are generated
  344.     *
  345.     * @access private
  346.     * @var    array
  347.     */
  348.     var $_valStack  =   array();
  349.  
  350.    /**
  351.     * current tag depth
  352.     *
  353.     * @access private
  354.     * @var    int
  355.     */
  356.     var $_depth = 0;
  357.  
  358.    /**
  359.     * XML_Parser instance
  360.     *
  361.     * @access   private
  362.     * @var      object XML_Parser
  363.     */
  364.     var $_parser = null;
  365.     
  366.    /**
  367.     * constructor
  368.     *
  369.     * @access   public
  370.     * @param    mixed   $options    array containing options for the unserialization
  371.     */
  372.     function XML_Unserializer($options = null)
  373.     {
  374.         if (is_array($options)) {
  375.             $this->options = array_merge($this->_defaultOptions, $options);
  376.         } else {
  377.             $this->options = $this->_defaultOptions;
  378.         }
  379.     }
  380.  
  381.    /**
  382.     * return API version
  383.     *
  384.     * @access   public
  385.     * @static
  386.     * @return   string  $version API version
  387.     */
  388.     function apiVersion()
  389.     {
  390.         return '0.18.0';
  391.     }
  392.  
  393.    /**
  394.     * reset all options to default options
  395.     *
  396.     * @access   public
  397.     * @see      setOption(), XML_Unserializer(), setOptions()
  398.     */
  399.     function resetOptions()
  400.     {
  401.         $this->options = $this->_defaultOptions;
  402.     }
  403.  
  404.    /**
  405.     * set an option
  406.     *
  407.     * You can use this method if you do not want to set all options in the constructor
  408.     *
  409.     * @access   public
  410.     * @see      resetOption(), XML_Unserializer(), setOptions()
  411.     */
  412.     function setOption($name, $value)
  413.     {
  414.         $this->options[$name] = $value;
  415.     }
  416.  
  417.    /**
  418.     * sets several options at once
  419.     *
  420.     * You can use this method if you do not want to set all options in the constructor
  421.     *
  422.     * @access   public
  423.     * @see      resetOption(), XML_Unserializer(), setOption()
  424.     */
  425.     function setOptions($options)
  426.     {
  427.         $this->options = array_merge($this->options, $options);
  428.     }
  429.  
  430.    /**
  431.     * unserialize data
  432.     *
  433.     * @access   public
  434.     * @param    mixed    $data     data to unserialize (string, filename or resource)
  435.     * @param    boolean  $isFile   data should be treated as a file
  436.     * @param    array    $options  options that will override the global options for this call
  437.     * @return   boolean  $success
  438.     */
  439.     function unserialize($data, $isFile = false, $options = null)
  440.     {
  441.         $this->_unserializedData = null;
  442.         $this->_root = null;
  443.  
  444.         // if options have been specified, use them instead
  445.         // of the previously defined ones
  446.         if (is_array($options)) {
  447.             $optionsBak = $this->options;
  448.             if (isset($options[XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS]) && $options[XML_UNSERIALIZER_OPTION_OVERRIDE_OPTIONS] == true) {
  449.                 $this->options = array_merge($this->_defaultOptions, $options);
  450.             } else {
  451.                 $this->options = array_merge($this->options, $options);
  452.             }
  453.         } else {
  454.             $optionsBak = null;
  455.         }
  456.  
  457.         $this->_valStack = array();
  458.         $this->_dataStack = array();
  459.         $this->_depth = 0;
  460.  
  461.         $this->_createParser();
  462.         
  463.         if (is_string($data)) {
  464.             if ($isFile) {
  465.                 $result = $this->_parser->setInputFile($data);
  466.                 if (PEAR::isError($result)) {
  467.                     return $result;
  468.                 }
  469.                 $result = $this->_parser->parse();
  470.             } else {
  471.                 $result = $this->_parser->parseString($data,true);
  472.             }
  473.         } else {
  474.            $this->_parser->setInput($data);
  475.            $result = $this->_parser->parse();
  476.         }
  477.  
  478.         if ($this->options[XML_UNSERIALIZER_OPTION_RETURN_RESULT] === true) {
  479.             $return = $this->_unserializedData;
  480.         } else {
  481.             $return = true;
  482.         }
  483.  
  484.         if ($optionsBak !== null) {
  485.             $this->options = $optionsBak;
  486.         }
  487.  
  488.         if (PEAR::isError($result)) {
  489.             return $result;
  490.         }
  491.  
  492.         return $return;
  493.     }
  494.  
  495.    /**
  496.     * get the result of the serialization
  497.     *
  498.     * @access public
  499.     * @return string  $serializedData
  500.     */
  501.     function getUnserializedData()
  502.     {
  503.         if ($this->_root === null) {
  504.             return $this->raiseError('No unserialized data available. Use XML_Unserializer::unserialize() first.', XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION);
  505.         }
  506.         return $this->_unserializedData;
  507.     }
  508.  
  509.    /**
  510.     * get the name of the root tag
  511.     *
  512.     * @access public
  513.     * @return string  $rootName
  514.     */
  515.     function getRootName()
  516.     {
  517.         if ($this->_root === null) {
  518.             return $this->raiseError('No unserialized data available. Use XML_Unserializer::unserialize() first.', XML_UNSERIALIZER_ERROR_NO_UNSERIALIZATION);
  519.         }
  520.         return $this->_root;
  521.     }
  522.  
  523.    /**
  524.     * Start element handler for XML parser
  525.     *
  526.     * @access private
  527.     * @param  object $parser  XML parser object
  528.     * @param  string $element XML element
  529.     * @param  array  $attribs attributes of XML tag
  530.     * @return void
  531.     */
  532.     function startHandler($parser, $element, $attribs)
  533.     {
  534.         if (isset($attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE]])) {
  535.             $type = $attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_TYPE]];
  536.             $guessType = false;
  537.         } else {
  538.             $type = 'string';
  539.             if ($this->options[XML_UNSERIALIZER_OPTION_GUESS_TYPES] === true) {
  540.                 $guessType = true;
  541.             } else {
  542.                 $guessType = false;
  543.             }
  544.         }
  545.  
  546.         if ($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC] !== null) {
  547.             $attribs = array_map($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC], $attribs);
  548.         }
  549.         
  550.         $this->_depth++;
  551.         $this->_dataStack[$this->_depth] = null;
  552.  
  553.         if (is_array($this->options[XML_UNSERIALIZER_OPTION_TAG_MAP]) && isset($this->options[XML_UNSERIALIZER_OPTION_TAG_MAP][$element])) {
  554.             $element = $this->options[XML_UNSERIALIZER_OPTION_TAG_MAP][$element];
  555.         }
  556.  
  557.         $val = array(
  558.                      'name'         => $element,
  559.                      'value'        => null,
  560.                      'type'         => $type,
  561.                      'guessType'    => $guessType,
  562.                      'childrenKeys' => array(),
  563.                      'aggregKeys'   => array()
  564.                     );
  565.  
  566.         if ($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE] == true && (count($attribs) > 0)) {
  567.             $val['children'] = array();
  568.             $val['type']  = $this->_getComplexType($element);
  569.             $val['class'] = $element;
  570.  
  571.             if ($this->options[XML_UNSERIALIZER_OPTION_GUESS_TYPES] === true) {
  572.                 $attribs = $this->_guessAndSetTypes($attribs);
  573.             }            
  574.             if ($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY] != false) {
  575.                 $val['children'][$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY]] = $attribs;
  576.             } else {
  577.                 foreach ($attribs as $attrib => $value) {
  578.                     $val['children'][$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTES_PREPEND].$attrib] = $value;
  579.                 }
  580.             }
  581.         }
  582.  
  583.         $keyAttr = false;
  584.         
  585.         if (is_string($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY])) {
  586.             $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY];
  587.         } elseif (is_array($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY])) {
  588.             if (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY][$element])) {
  589.                 $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY][$element];
  590.             } elseif (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]['#default'])) {
  591.                 $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]['#default'];
  592.             } elseif (isset($this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]['__default'])) {
  593.                 // keep this for BC
  594.                 $keyAttr = $this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_KEY]['__default'];
  595.             }
  596.         }
  597.         
  598.         if ($keyAttr !== false && isset($attribs[$keyAttr])) {
  599.             $val['name'] = $attribs[$keyAttr];
  600.         }
  601.  
  602.         if (isset($attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS]])) {
  603.             $val['class'] = $attribs[$this->options[XML_UNSERIALIZER_OPTION_ATTRIBUTE_CLASS]];
  604.         }
  605.  
  606.         array_push($this->_valStack, $val);
  607.     }
  608.  
  609.    /**
  610.     * Try to guess the type of several values and
  611.     * set them accordingly
  612.     *
  613.     * @access   private
  614.     * @param    array      array, containing the values
  615.     * @return   array      array, containing the values with their correct types 
  616.     */
  617.     function _guessAndSetTypes($array)
  618.     {
  619.         foreach ($array as $key => $value) {
  620.             $array[$key] = $this->_guessAndSetType($value);
  621.         }
  622.         return $array;
  623.     }
  624.     
  625.    /**
  626.     * Try to guess the type of a value and
  627.     * set it accordingly
  628.     *
  629.     * @access   private
  630.     * @param    string      character data
  631.     * @return   mixed       value with the best matching type
  632.     */
  633.     function _guessAndSetType($value)
  634.     {
  635.         if ($value === 'true') {
  636.             return true;
  637.         }
  638.         if ($value === 'false') {
  639.             return false;
  640.         }
  641.         if ($value === 'NULL') {
  642.             return null;
  643.         }
  644.         if (preg_match('/^[-+]?[0-9]{1,}$/', $value)) {
  645.             return intval($value);
  646.         }
  647.         if (preg_match('/^[-+]?[0-9]{1,}\.[0-9]{1,}$/', $value)) {
  648.             return doubleval($value);
  649.         }
  650.         return (string)$value;
  651.     }
  652.     
  653.    /**
  654.     * End element handler for XML parser
  655.     *
  656.     * @access private
  657.     * @param  object XML parser object
  658.     * @param  string
  659.     * @return void
  660.     */
  661.     function endHandler($parser, $element)
  662.     {
  663.         $value = array_pop($this->_valStack);
  664.         switch ($this->options[XML_UNSERIALIZER_OPTION_WHITESPACE]) {
  665.             case XML_UNSERIALIZER_WHITESPACE_KEEP:
  666.                 $data = $this->_dataStack[$this->_depth];
  667.                 break;
  668.             case XML_UNSERIALIZER_WHITESPACE_NORMALIZE:
  669.                 $data = trim(preg_replace('/\s\s+/m', ' ', $this->_dataStack[$this->_depth]));
  670.                 break;
  671.             case XML_UNSERIALIZER_WHITESPACE_TRIM:
  672.             default:
  673.                 $data  = trim($this->_dataStack[$this->_depth]);
  674.                 break;
  675.         }
  676.  
  677.         // adjust type of the value
  678.         switch(strtolower($value['type'])) {
  679.  
  680.             // unserialize an object
  681.             case 'object':
  682.                 if (isset($value['class'])) {
  683.                     $classname  = $value['class'];
  684.                 } else {
  685.                     $classname = '';
  686.                 }
  687.                 // instantiate the class
  688.                 if ($this->options[XML_UNSERIALIZER_OPTION_TAG_AS_CLASSNAME] === true && class_exists($classname)) {
  689.                     $value['value'] = &new $classname;
  690.                 } else {
  691.                     $value['value'] = &new $this->options[XML_UNSERIALIZER_OPTION_DEFAULT_CLASS];
  692.                 }
  693.                 if (trim($data) !== '') {
  694.                     if ($value['guessType'] === true) {
  695.                         $data = $this->_guessAndSetType($data);
  696.                     }
  697.                     $value['children'][$this->options[XML_UNSERIALIZER_OPTION_CONTENT_KEY]] = $data;
  698.                 }
  699.  
  700.                 // set properties
  701.                 foreach ($value['children'] as $prop => $propVal) {
  702.                     // check whether there is a special method to set this property
  703.                     $setMethod = 'set'.$prop;
  704.                     if (method_exists($value['value'], $setMethod)) {
  705.                         call_user_func(array(&$value['value'], $setMethod), $propVal);
  706.                     } else {
  707.                         $value['value']->$prop = $propVal;
  708.                     }
  709.                 }
  710.                 //  check for magic function
  711.                 if (method_exists($value['value'], '__wakeup')) {
  712.                     $value['value']->__wakeup();
  713.                 }
  714.                 break;
  715.  
  716.             // unserialize an array
  717.             case 'array':
  718.                 if (trim($data) !== '') {
  719.                     if ($value['guessType'] === true) {
  720.                         $data = $this->_guessAndSetType($data);
  721.                     }
  722.                     $value['children'][$this->options[XML_UNSERIALIZER_OPTION_CONTENT_KEY]] = $data;
  723.                 }
  724.                 if (isset($value['children'])) {
  725.                     $value['value'] = $value['children'];
  726.                 } else {
  727.                     $value['value'] = array();
  728.                 }
  729.                 break;
  730.  
  731.             // unserialize a null value
  732.             case 'null':
  733.                 $data = null;
  734.                 break;
  735.  
  736.             // unserialize a resource => this is not possible :-(
  737.             case 'resource':
  738.                 $value['value'] = $data;
  739.                 break;
  740.  
  741.             // unserialize any scalar value
  742.             default:
  743.                 if ($value['guessType'] === true) {
  744.                     $data = $this->_guessAndSetType($data);
  745.                 } else {
  746.                     settype($data, $value['type']);
  747.                 }
  748.             
  749.                 $value['value'] = $data;
  750.                 break;
  751.         }
  752.         $parent = array_pop($this->_valStack);
  753.         if ($parent === null) {
  754.             $this->_unserializedData = &$value['value'];
  755.             $this->_root = &$value['name'];
  756.             return true;
  757.         } else {
  758.             // parent has to be an array
  759.             if (!isset($parent['children']) || !is_array($parent['children'])) {
  760.                 $parent['children'] = array();
  761.                 if (!in_array($parent['type'], array('array', 'object'))) {
  762.                     $parent['type'] = $this->_getComplexType($parent['name']);
  763.                     if ($parent['type'] == 'object') {
  764.                         $parent['class'] = $parent['name'];
  765.                     }
  766.                 }
  767.             }
  768.  
  769.             if (in_array($element, $this->options[XML_UNSERIALIZER_OPTION_IGNORE_KEYS])) {
  770.                 $ignoreKey = true;
  771.             } else {
  772.                 $ignoreKey = false;
  773.             }
  774.             
  775.             if (!empty($value['name']) && $ignoreKey === false) {
  776.                 // there already has been a tag with this name
  777.                 if (in_array($value['name'], $parent['childrenKeys']) || in_array($value['name'], $this->options[XML_UNSERIALIZER_OPTION_FORCE_ENUM])) {
  778.                     // no aggregate has been created for this tag
  779.                     if (!in_array($value['name'], $parent['aggregKeys'])) {
  780.                         if (isset($parent['children'][$value['name']])) {
  781.                             $parent['children'][$value['name']] = array($parent['children'][$value['name']]);
  782.                         } else {
  783.                             $parent['children'][$value['name']] = array();
  784.                         }
  785.                         array_push($parent['aggregKeys'], $value['name']);
  786.                     }
  787.                     array_push($parent['children'][$value['name']], $value['value']);
  788.                 } else {
  789.                     $parent['children'][$value['name']] = &$value['value'];
  790.                     array_push($parent['childrenKeys'], $value['name']);
  791.                 }
  792.             } else {
  793.                 array_push($parent['children'], $value['value']);
  794.             }
  795.             array_push($this->_valStack, $parent);
  796.         }
  797.  
  798.         $this->_depth--;
  799.     }
  800.  
  801.    /**
  802.     * Handler for character data
  803.     *
  804.     * @access private
  805.     * @param  object XML parser object
  806.     * @param  string CDATA
  807.     * @return void
  808.     */
  809.     function cdataHandler($parser, $cdata)
  810.     {
  811.         if ($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC] !== null) {
  812.             $cdata = call_user_func($this->options[XML_UNSERIALIZER_OPTION_DECODE_FUNC], $cdata);
  813.         }
  814.         $this->_dataStack[$this->_depth] .= $cdata;
  815.     }
  816.  
  817.    /**
  818.     * get the complex type, that should be used for a specified tag
  819.     *
  820.     * @access   private
  821.     * @param    string      name of the tag
  822.     * @return   string      complex type ('array' or 'object')
  823.     */
  824.     function _getComplexType($tagname)
  825.     {
  826.         if (is_string($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE])) {
  827.             return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE];
  828.         }
  829.         if (isset($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE][$tagname])) {
  830.             return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE][$tagname];
  831.         }
  832.         if (isset($this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]['#default'])) {
  833.             return $this->options[XML_UNSERIALIZER_OPTION_COMPLEXTYPE]['#default'];
  834.         }
  835.         return 'array';
  836.     }
  837.     
  838.    /**
  839.     * create the XML_Parser instance
  840.     *
  841.     * @access    private
  842.     * @return    boolean
  843.     */
  844.     function _createParser()
  845.     {
  846.         if (is_object($this->_parser)) {
  847.             $this->_parser->free();
  848.             unset($this->_parser);
  849.         }
  850.         $this->_parser = &new XML_Parser($this->options[XML_UNSERIALIZER_OPTION_ENCODING_SOURCE], 'event', $this->options[XML_UNSERIALIZER_OPTION_ENCODING_TARGET]);
  851.         $this->_parser->folding = false;
  852.         $this->_parser->setHandlerObj($this);
  853.         return true;
  854.     }
  855. }
  856. ?>